﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Autofac;
using Autofac.Builder;
using Autofac.Features.Scanning;
using Inovout.Data.Domain;
using Inovout.Data.Repository;
using Inovout.Data.Service;
using NHibernate;
using NHibernate.Cfg;

namespace Inovout.Data
{
    public static class RegistrationExtensions
    {
        public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
             RegisterRepositories(this ContainerBuilder builder, params Assembly[] moduleAssemblies)
        {
            builder.Register(c =>
            {
                Configuration configuration = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs/Hibernate.cfg.xml"));
                //   .ConfigureFullTextSearch();
                foreach (Assembly assembly in moduleAssemblies)
                {
                    configuration.AddAssembly(assembly);
                    var dataTypeFullNams = assembly.GetTypes().Where(t => typeof(DataObject).IsAssignableFrom(t)).Select(t => t.FullName);
                    var hbmXmlResources = assembly.GetManifestResourceNames().Intersect(dataTypeFullNams);
                    if (hbmXmlResources.Count() > 0)
                    {
                        configuration.AddResources(hbmXmlResources, assembly);
                    }
                }
                return configuration;
            })
            .SingleInstance();


            builder.Register(c => c.Resolve<Configuration>().BuildSessionFactory())
                .SingleInstance();

            builder.Register(c => new TransactionTracker(c.Resolve<ExceptionContext>()))
                   .InstancePerLifetimeScope();

            builder.Register(c => c.Resolve<ISessionFactory>().OpenSession())
                 .InstancePerLifetimeScope()
                 .OnActivated(e => e.Context.Resolve<TransactionTracker>().BeginTransaction(e.Instance));

            builder.Register(c => c.Resolve<ISessionFactory>().OpenStatelessSession())
         .InstancePerLifetimeScope();



            #region Repository
            //RegisterAssemblyType不支持泛型查找
            builder.RegisterGeneric(typeof(DataObjectRepository<>))
                   .As(typeof(IRepository<>))
                   .InstancePerLifetimeScope()
                   //.InstancePerLifetimeScope()
                   .OnActivated(ah => ActivateRepository(ah.Context, ah.Instance));
            builder.RegisterGeneric(typeof(DataObjectRepository<>))
                            .As(typeof(IQueryableRepository<>))
                            .InstancePerLifetimeScope()
                            .OnActivated(ah => ActivateRepository(ah.Context, ah.Instance));

            builder.RegisterAssemblyTypes(moduleAssemblies)
                   .Where(t => typeof(IRepository).IsAssignableFrom(t))
                  //.As(typeof(IRepository<>))
                  .AsImplementedInterfaces()
                  .AsSelf()
                   .InstancePerLifetimeScope()
                  .OnActivated(ah => ActivateRepository(ah.Context, ah.Instance));

            #endregion

            #region RepositoryService
            builder.RegisterGeneric(typeof(RepositoryService<>))
                 .As(typeof(IRepositoryService<>))
                 .As(typeof(IRepositoryForControllerService<>))
                 .InstancePerLifetimeScope()
                 .OnActivated(ah => ActivateRepositoryService(ah.Context, ah.Instance));

            return builder.RegisterAssemblyTypes(moduleAssemblies)
                   .Where(t => typeof(IRepositoryService).IsAssignableFrom(t))
                   //.As(typeof(IRepositoryService<>))
                   .AsImplementedInterfaces()
                   .AsSelf()
                   .InstancePerLifetimeScope()
                   .OnActivated(ah => ActivateRepositoryService(ah.Context, ah.Instance));


            #endregion
        }
        private static void ActivateRepository(IComponentContext context, object activatedRepository)
        {
            BaseRepository repository = activatedRepository as BaseRepository;
            if (repository != null)
            {
                repository.Session = context.Resolve<ISession>();
                repository.StatelessSession = context.Resolve<IStatelessSession>();
            }
        }

        private static void ActivateRepositoryService(IComponentContext context, object activatedRepositoryService)
        {
            RepositoryService repositoryService = activatedRepositoryService as RepositoryService;
            if (repositoryService != null)
            {
                Type modelType = GetRepositoryServiceModelType(repositoryService.GetType());
                Type repositoryType = typeof(IRepository<>);
                repositoryType = repositoryType.MakeGenericType(modelType);
                repositoryService.repository = (BaseRepository)context.Resolve(repositoryType);
            }
        }
        private static Type GetRepositoryServiceModelType(Type repositoryServiceType)
        {
            Type repositoryServiceInterfaceType = repositoryServiceType.GetInterface(typeof(IRepositoryService<>).Name);

            if (repositoryServiceInterfaceType == null)
            {
                return null;
            }
            else
            {
                return repositoryServiceInterfaceType.GetGenericArguments()[0];
            }
        }
        public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
             RegisterDomains(this ContainerBuilder builder, params Assembly[] domainAssemblies)
        {
            return builder.RegisterAssemblyTypes(domainAssemblies)
                .Where(t => typeof(DataObject).IsAssignableFrom(t))
                .AsSelf()
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .UsingConstructor();
                //.PropertiesAutowired(PropertyWiringOptions.PreserveSetValues);
        }
    }
}
